//
// Created by Administrator on 2020/10/29.
//

#include <sys/epoll.h>
#include "../../include/utils/poller.h"

typedef struct
{
    int fd;
    int32_t inUse;
    PollEventHandle inHandle;
    PollEventHandle outHandle;
    PollEventHandle otherHandle;
    void *data;
} S_PollEventNode;

struct Poller
{
    int epfd;
    uint32_t eventNodeNum;
    S_PollEventNode *pEventNodes;
    struct epoll_event *pEventList;
};

S_Poller *POLLER_Create(uint32_t fdNumMax)
{
    S_Poller *self;

    self = OS_MemCalloc(1, sizeof(S_Poller));
    if (NULL == self)
    {
        return NULL;
    }
    self->eventNodeNum = fdNumMax;
    self->pEventNodes = OS_MemCalloc(1, fdNumMax * sizeof(S_PollEventNode));
    if (NULL == self->pEventNodes)
    {
        OS_MemFree(self);
        return NULL;
    }
    self->pEventList = OS_MemCalloc(1, fdNumMax * sizeof(struct epoll_event));
    if (NULL == self->pEventList)
    {
        OS_MemFree(self->pEventNodes);
        OS_MemFree(self);
        return NULL;
    }
    self->epfd = epoll_create(fdNumMax);
    if (-1 == self->epfd)
    {
        OS_MemFree(self->pEventNodes);
        OS_MemFree(self->pEventList);
        OS_MemFree(self);
        return NULL;
    }

    return self;
}

void POLLER_Destroy(S_Poller *self)
{
    close(self->epfd);
    OS_MemFree(self->pEventNodes);
    OS_MemFree(self->pEventList);
    OS_MemFree(self);
}

int32_t POLLER_Add(S_Poller *self, int fd, PollEventHandle inHandle,
    PollEventHandle outHandle, PollEventHandle otherHandle, void *data)
{
    int32_t rc;
    uint32_t loop;
    struct epoll_event ee;
    S_PollEventNode *pollEventNode;

    pollEventNode = NULL;
    for (loop = 0; loop < self->eventNodeNum; loop++)
    {
        if (!self->pEventNodes[loop].inUse)
        {
            pollEventNode = &self->pEventNodes[loop];
            break;
        }
    }
    if (NULL == pollEventNode)
    {
        return CSP_RC_ER;
    }
    pollEventNode->inUse = 1;
    pollEventNode->fd = fd;
    pollEventNode->inHandle = inHandle;
    pollEventNode->outHandle = outHandle;
    pollEventNode->otherHandle = otherHandle;
    pollEventNode->data = data;

    ee.events = 0;
    if (inHandle)
    {
        ee.events |= EPOLLIN;
    }
    if (outHandle)
    {
        ee.events |= EPOLLOUT;
    }
    if (otherHandle)
    {
        ee.events |= EPOLLHUP;
    }
    ee.data.ptr = pollEventNode;

    rc = epoll_ctl(self->epfd, EPOLL_CTL_ADD, fd, &ee);
    if (rc < 0)
    {
        return CSP_RC_ER;
    }

    return CSP_RC_OK;
}

int32_t POLLER_Remove(S_Poller *self, int fd)
{
    int32_t rc;
    uint32_t loop;
    struct epoll_event ee;
    S_PollEventNode *pollEventNode;

    pollEventNode = NULL;
    for (loop = 0; loop < self->eventNodeNum; loop++)
    {
        if (self->pEventNodes[loop].inUse && fd == self->pEventNodes[loop].fd)
        {
            pollEventNode = &self->pEventNodes[loop];
            break;
        }
    }
    if (NULL == pollEventNode)
    {
        return CSP_RC_ER;
    }

    pollEventNode->inUse = 0;

    ee.events = 0;
    if (pollEventNode->inHandle)
    {
        ee.events |= EPOLLIN;
    }
    if (pollEventNode->outHandle)
    {
        ee.events |= EPOLLOUT;
    }
    if (pollEventNode->otherHandle)
    {
        ee.events |= EPOLLHUP;
    }
    ee.data.ptr = NULL;

    rc = epoll_ctl(self->epfd, EPOLL_CTL_DEL, pollEventNode->fd, &ee);
    if (rc < 0)
    {
        return CSP_RC_ER;
    }

    return CSP_RC_OK;
}

int32_t POLLER_Poll(S_Poller *self, int32_t timeoutMs)
{
    int32_t rc;
    int32_t loop;
    S_PollEventNode *pollEventNode;

    rc = epoll_wait(self->epfd, self->pEventList, self->eventNodeNum, timeoutMs);
    if (rc <= 0)
    {
        return rc;
    }
    for (loop = 0; loop < rc; loop++)
    {
        if (self->pEventList[loop].events)
        {
            pollEventNode = (S_PollEventNode *)self->pEventList[loop].data.ptr;
        }
        if (self->pEventList[loop].events & EPOLLIN)
        {
            if (pollEventNode->inHandle)
            {
                pollEventNode->inHandle(EPOLLIN, pollEventNode->fd, pollEventNode->data);
            }
            self->pEventList[loop].events = self->pEventList[loop].events & (~EPOLLIN);
        }
        if (self->pEventList[loop].events & EPOLLOUT)
        {
            if (pollEventNode->outHandle)
            {
                pollEventNode->outHandle(EPOLLOUT, pollEventNode->fd, pollEventNode->data);
            }
            self->pEventList[loop].events = self->pEventList[loop].events & (~EPOLLOUT);
        }
        if (self->pEventList[loop].events)
        {
            if (pollEventNode->otherHandle)
            {
                pollEventNode->otherHandle(self->pEventList[loop].events, pollEventNode->fd, pollEventNode->data);
            }
        }
    }
    return rc;
}
